package JavaCollection.mapLearn;




import org.junit.jupiter.api.Test;

import java.util.*;

/**
 * @author Lenl
 * @version v1.0
 * @create 2021-06-05 13:03
 * @description
 * |--Map双列数据，存储key-value对的数据  类似于y=f(x)
 *    |--HashMap:作为Map的主要实现类,线程不安全，效率高；可存储null的key和value
 *      |--LinkedHashMap:保证遍历map元素时，是按照添加的顺序遍历的
 *          原因：底层结构加了链表结构。对于频繁遍历，执行效率高于HashMap
 *    |--TreeMap:保证按照添加的key-value对进行排序，实现排序遍历，此时考虑key的自然排序后定制排序
 *          底层使用红黑树
 *    |--Hashtable:作为古老的实现类，线程安全，效率低；不能存储bull的key和value
 *      |--Properties:常用来处理配置文件。key和value都是String类型
 * HashMap底层：数组+链表（jdk7以前）
 *             数组+链表+红黑树（jdk8）
 * 面试题：
 * 1.HashMap的底层实现原理？
 * 2.HashMap和Hashtable的异同
 * 3.CurrentHashMap与Hashtable的异同？（暂时不讲）
 *
 * 二、Map结构的理解
 *      Map中的key:无序的，不可重复的，使用Set存储所有的key-->key所在类要重写equals和hashCode() (以HashMap为例)
 *      Map中的value:无序的、可重复的，使用Collection存储所有的value-->value所在类要重写equals
 *      一个键值对key-value构成了一个Entry对象
 *      Map中的Entry:无序的、不可重复的，使用Set存储所有的entry
 *
 * 三、HashMap的底层实现原理？以jdk7为例说明
 *      HashMap map=new HashMap();
 *      实例化后，底层创建了长度为16的一堆数组 Entry[] table
 *      ...执行过多次put...
 *      map.put(k1,v1)
 *      首先调用k1所在类的hashCode()计算k1的哈希值，经过计算后得到在Entry数组的存放位置
 *      如果此位置上数据为空，则此时k1-v1添加成功 -->情况1
 *      如果此位置数据不为空，（意味着此位置存在一个或多个数据(链表形式)），比较k1和已存在的一个或多个数据哈希值
 *          如果k1与已存在的数据哈希值都不同，此时k1-v1添加成功 -->情况2
 *          如果存在相同(k2-v2)，继续比较：调用k1所在类的equals()方法，比较
 *              如果equals()返回false:添加k1-v1成功 -->情况3
 *              如果equals()返回true:则使用v1替换v2
 *      补充：关于情况2和情况3：此时k1-v1和原来的数据以链表方式存储
 *
 *      在不断添加过程中会涉及到扩容问题，当超出临界值（且要存放的位置非空）时扩容，默认扩容2倍，并将原有数据复制过来
 *
 *     jdk8相较于jdk7底层实现的不同：
 *     1.new HashMap():底层没有创建一个长度为16的数组
 *     2.jdk8底层数组是Node[]而非Entry[]
 *     3.首次调用put()方法时，底层创建长度为16的数组
 *     4.jdk7底层只有：数组+链表。jdk8中底层结构：数组+链表+红黑树
 *        当数组某一索引位置上的元素以链表形式存在的数据个数>8且当前数组长度>64时，
 *        此时索引位置上的所有数据改为使用红黑树存储
 *
 *      DEFAULT_INITIAL_CAPACITY:HashMap的默认容量：16
 *      DEFAULT_LOAD_FACTOR:HashMap的默认加载因子：0.75
 *      threshold:扩容的临界值=容量*填充因子：16*0.75=12
 *      TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值，转化为红黑树：8
 *      MIN_TREEIFY_CAPACITY:桶中Node被树化时最小的hash表容量：64
 *
 *  四、LinkedHashMap的底层实现原理（了解）
 *      源码中：
 *          static class Entry<K,V> extends HashMap.Node<K,V> {
 *         Entry<K,V> before, after; //能够记录添加的元素的先后顺序
 *         Entry(int hash, K key, V value, Node<K,V> next) {
 *             super(hash, key, value, next);
 *           }
 *        }
 *
 * 五、Map中定义的方法：
 *
 *
 */
public class MapTest {
    @Test
    public void test1(){
        Map map=new HashMap();
        //map=new Hashtable();//Hashtable装null会报空指针异常
        map.put(null,null);
    }
    @Test
    public void test2(){
        Map map=new HashMap();

        map=new LinkedHashMap();//这个按添加顺序遍历

        map.put(29,"12312");
        map.put(1,"zhangsan");
        map.put(2,"lisi");
        map.put(3,"wangwu");

        System.out.println(map);
    }

    /**
     * map中的常用的增删改方法
     */
    @Test
    public void test3(){
        Map map=new HashMap();
        //put
        map.put("aa",123);
        map.put("bb",321);
        map.put("aa",000);//这里会覆盖掉第一个，算一个修改操作
        map.put("cc",999);
        System.out.println(map);
        //putAll
        Map mp2=new HashMap();
        mp2.put("oo",456);
        mp2.put("qq",255);
        map.putAll(mp2);
        System.out.println(map);
        //remove(Object key)
        Object value=map.remove("qq");//返回的是对应的value,不存在则返回null
        System.out.println(value+"|"+map);
        //clear()
        mp2.clear();
        System.out.println(mp2);
        System.out.println(mp2.size());//清空数据，不会空指针,与mp2=null不同
    }

    /**
     * map中常用的查询操作
     */
    @Test
    public void test4(){
        Map map=new HashMap();
        map.put("aa",123);
        map.put("bb",321);
        map.put("aa",000);//这里会覆盖掉第一个，算一个修改操作
        map.put("cc",999);

        //get()
        System.out.println(map.get("cc"));
        System.out.println(map.get("sda"));//null

        //containsKey(object)|containsValue(object)
        System.out.println(map.containsKey("cc"));
        System.out.println(map.containsValue(123));//false

        //int size()
        System.out.println(map.size());

        //int isEmpty()
        System.out.println(map.isEmpty());

        // boolean equals(object)//判断当前map对象与参数obj是否相等
        Map mp2=new HashMap();
        System.out.println(map.equals(mp2));

    }

    /**
     * 元视图的操作方法
     *
     */
    @Test
    public void test5(){
        Map map=new HashMap();
        map.put("aa",123);
        map.put("bb",321);
        map.put("aa",000);//这里会覆盖掉第一个，算一个修改操作
        map.put("cc",999);

        //遍历所有的key集 keySet()
        Set set=map.keySet();
        Iterator iterator=set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

        //遍历所有的value集 values()
        Collection coll=map.values();
        Iterator iterator1=coll.iterator();
        while (iterator1.hasNext()){
            System.out.println(iterator1.next());
        }

        //遍历所有的key-value: entrySet()
        Set entrys=map.entrySet();
        Iterator iterator2=entrys.iterator();
        while (iterator2.hasNext()){
            //System.out.println(iterator2.next());
            //每个元素否是Entry
            Map.Entry entry=(Map.Entry)iterator2.next();
            System.out.println(entry.getKey()+":"+entry.getValue());
        }

        //方式二
        Iterator iterator3=set.iterator();
        while (iterator3.hasNext()){
            Object key=iterator3.next();
            System.out.println(key+"-->"+map.get(key));
        }


    }

}
